package controlmercancias.remoto.cliente;

import controlmercancias.util.Message;
import controlmercancias.util.SocketManager;
import controlmercancias.excepciones.ForceExitException;
import java.awt.Image;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.ImageIcon;

/**
 *
 * @author Hasier
 */
public class Cliente {

    public static final Image appIcon = new ImageIcon(Cliente.class.getResource("/controlmercancias/resources/icono.png")).getImage();
    private static Cliente instance;
    private String user;
    private String ip;
    private final int port = 8089;
    private SocketManager sm;
    private List<String> trenes;
    private String trenActual;

    private Cliente(String user, String ip) throws IOException {
        this.user = user;
        this.ip = ip;
        sm = new SocketManager(this.ip, this.port);
    }

    public static String getUserName() {
        return getInstance().user;
    }

    public static void createCliente(String user, String ip) throws IOException {
        instance = new Cliente(user, ip);
    }

    private static Cliente getInstance() {
        return instance;
    }

    private String internalGetTrenActual() {
        return trenActual;
    }

    public static String getTrenActual() {
        return getInstance().internalGetTrenActual();
    }

    private String internalValidateUser() throws IOException, ForceExitException {
        sm.write(("USER " + user).trim());
        String line = readLine();
        if (line.contains(Message.OK.toString())) {
            return Message.OK.toString();
        } else if (line.contains("499")) {
            return "Se ha rechazado tu petición, servidor sobrecargado";
        } else {
            return "Nombre de usuario incorrecto";
        }
    }

    public static String validateUser() throws ForceExitException, IOException {
        try {
            return getInstance().internalValidateUser();
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalValidatePass(String pass) throws IOException, ForceExitException {
        sm.write(("PASS " + pass).trim());
        String line = readLine();
        if (line.contains(Message.OK.toString())) {
            internalReceiveTrenesDeUsuario();
            return Message.OK.toString();
        } else if (line.contains("412")) {
            return "Clave incorrecta";
        } else if (line.contains("434")) {
            return "Todos tus trenes están en uso. Inténtalo más tarde";
        } else if (line.contains("435")) {
            return "No tienes trenes asignados. Habla con tu administrador para poder acceder";
        } else if (line.contains("436")) {
            return "El usuario " + getUserName() + " ya está en el sistema";
        } else {
            return "Falta la clave";
        }
    }

    public static String validatePass(String pass) throws ForceExitException, IOException {
        try {
            return getInstance().internalValidatePass(pass);
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalSetTren(String id) throws IOException, ForceExitException {
        sm.write("SET_TREN " + id);
        String line = readLine();
        if (line.contains(Message.OK.toString())) {
            trenActual = id;
            return Message.OK.toString();
        } else if (line.contains("432")) {
            return "El tren con id " + id + " no existe";
        } else if (line.contains("433")) {
            return "El tren con id " + id + " ya está en uso por otro usuario";
        } else {
            return "Falta el parámetro id";
        }
    }

    public static String setTren(String id) throws ForceExitException, IOException {
        try {
            return getInstance().internalSetTren(id);
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private List<String> internalReceiveTrenesDeUsuario() throws IOException, ForceExitException {
        readLine();
        StringTokenizer st = new StringTokenizer(getLines(), "\n");
        trenes = new ArrayList<String>();
        while (st.hasMoreElements()) {
            trenes.add(st.nextToken());
        }
        if (!trenes.isEmpty()) {
            trenActual = trenes.get(0);
        } else {
            trenActual = null;
        }
        return trenes;
    }

    private List<String> internalGetTrenesDeUsuario() throws IOException, ForceExitException {
        sm.write("TRENES");
        return internalReceiveTrenesDeUsuario();
    }

    public static List<String> getTrenesDeUsuario() throws ForceExitException, IOException {
        try {
            return getInstance().internalGetTrenesDeUsuario();
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private List<String> internalListsensor() throws IOException, ForceExitException {
        sm.write("LISTSENSOR");
        readLine();
        StringTokenizer st = new StringTokenizer(getLines(), "\n");
        ArrayList<String> sensores = new ArrayList<String>(st.countTokens());
        while (st.hasMoreElements()) {
            sensores.add(st.nextToken());
        }
        return sensores;
    }

    public static List<String> listsensor() throws ForceExitException, IOException {
        try {
            return getInstance().internalListsensor();
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalHistorico(int id) throws IOException, ForceExitException {
        sm.write(("HISTORICO " + id).trim());
        String line = readLine();
        if (line.contains(Message.OK.toString())) {
            return getLines();
        } else if (line.contains("424")) {
            return "Sensor desconocido";
        } else {
            return "Falta el parámetro id";
        }
    }

    public static String historico(int id) throws ForceExitException, IOException {
        try {
            return getInstance().internalHistorico(id);
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalOn(int id) throws IOException, ForceExitException {
        sm.write(("ON " + id).trim());
        String line = readLine();
        if (line.contains(Message.OK.toString())) {
            return Message.OK.toString();
        } else if (line.contains("427")) {
            return "El sensor con id " + id + " no existe";
        } else {
            return "El sensor con id " + id + " ya está encendido";
        }
    }

    public static String on(int id) throws ForceExitException, IOException {
        try {
            return getInstance().internalOn(id);
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalOff(int id) throws IOException, ForceExitException {
        sm.write(("OFF " + id).trim());
        String line = readLine();
        if (line.contains(Message.OK.toString())) {
            return Message.OK.toString();
        } else if (line.contains("427")) {
            return "El sensor con id " + id + " no existe";
        } else {
            return "El sensor con id " + id + " ya está apagado";
        }
    }

    public static String off(int id) throws ForceExitException, IOException {
        try {
            return getInstance().internalOff(id);
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalOnGps() throws IOException, ForceExitException {
        sm.write("ONGPS");
        String line = readLine();
        if (line.contains(Message.OK.toString())) {
            return Message.OK.toString();
        } else {
            return "El GPS ya está encendido";
        }
    }

    public static String onGps() throws ForceExitException, IOException {
        try {
            return getInstance().internalOnGps();
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalOffGps() throws IOException, ForceExitException {
        sm.write("OFFGPS");
        String line = readLine();
        if (line.contains(Message.OK.toString())) {
            return Message.OK.toString();
        } else {
            return "El GPS ya está apagado";
        }
    }

    public static String offGps() throws ForceExitException, IOException {
        try {
            return getInstance().internalOffGps();
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalGetGps() throws IOException, ForceExitException {
        sm.write("GPS");
        String line = readLine();
        StringTokenizer st = new StringTokenizer(line, " ");
        String s = null;
        while (st.hasMoreElements()) {
            s = st.nextToken();
        }
        return s.trim();
    }

    public static String getGps() throws ForceExitException, IOException {
        try {
            return getInstance().internalGetGps();
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalGetPosGps() throws IOException, ForceExitException {
        sm.write("GET_LOC");
        String line = readLine();
        if (line.contains(Message.OK.toString())) {
            StringTokenizer st = new StringTokenizer(line, " ");
            String s = null;
            while (st.hasMoreElements()) {
                s = st.nextToken();
            }
            return s.trim();
        } else {
            return "GPS no conectado";
        }
    }

    public static String getPosGps() throws ForceExitException, IOException {
        try {
            return getInstance().internalGetPosGps();
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalGetValact(int id) throws IOException, ForceExitException {
        sm.write(("GET_VALACT " + id).trim());
        String line = readLine();
        if (line.contains(Message.OK.toString())) {
            return line.substring(line.indexOf(Message.OK.toString()) + 3).replace("�", "º").trim();
        } else if (line.contains("424")) {
            return "El sensor con id " + id + " no existe";
        } else if (line.contains("425")) {
            return "Falta el parámetro id";
        } else {
            return "El sensor con id " + id + " está apagado";
        }
    }

    public static String getValact(int id) throws ForceExitException, IOException {
        try {
            return getInstance().internalGetValact(id);
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalActualizar(String user, String pass) throws IOException, ForceExitException, Exception {
        sm.write("ACTUALIZAR");
        sm.write(user);
        sm.write(pass);
        String line = readLine();
        if (line.contains(Message.OK.toString())) {
            return "Actualización de configuración finalizada con éxito";
        } else if (line.contains("430")) {
            throw new Exception("El tren no está en estado de mantenimiento");
        } else if (line.contains("437")) {
            throw new Exception("No se puede guardar la actualización en esta unidad");
        } else if (line.contains("438")) {
            throw new IOException();
        } else {
            throw new Exception(line.substring(line.indexOf("ERR") + 4).trim());
        }
    }

    public static String actualizar(String user, String pass) throws ForceExitException, IOException, Exception {
        try {
            return getInstance().internalActualizar(user, pass);
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalEstado() throws IOException, ForceExitException {
        sm.write("ESTADO");
        String line = readLine();
        StringTokenizer st = new StringTokenizer(line, " ");
        String s = null;
        while (st.hasMoreElements()) {
            s = st.nextToken();
        }
        return s.trim();
    }

    public static String estado() throws ForceExitException, IOException {
        try {
            return getInstance().internalEstado();
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String internalSalir() throws IOException, ForceExitException {
        sm.write("SALIR");
        readLine();
        return Message.OK.toString();
    }

    public static String salir() throws ForceExitException, IOException {
        try {
            return getInstance().internalSalir();
        } catch (IOException ex) {
            throw new IOException(Message.IOException.toString());
        }
    }

    private String getLines() throws IOException, ForceExitException {
        String resultado = "";
        String line;
        while (!(line = readLine()).contains("222")) {
            resultado += line + "\n";
        }
        return resultado;
    }

    private String readLine() throws IOException, ForceExitException {
        String line = sm.readLine();
        if (line.contains(Message.ForceExit.toString())) {
            throw new ForceExitException("El administrador te ha expulsado");
        }
        return line.trim();
    }
}
